package com.dm.cloud.file.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.net.URLEncodeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.dm.cloud.core.exception.CustomException;
import com.dm.cloud.file.common.model.file.*;
import com.dm.cloud.file.conf.LocalUtil;
import com.dm.cloud.file.model.CommonProp;
import com.dm.cloud.file.service.FIleManagerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;

@Service
public class FIleManagerImpl implements FIleManagerService {

    @Autowired
    private LocalUtil localUtil;

    @Override
    public List<FileModel> files(String parent, String filterName, FileModel.FileType fileType) {
        File sourceDir = localUtil.getFileDir();
        String activatePath = null;
        if (StrUtil.isEmpty(parent)) {
            activatePath = "/";
        } else {
            activatePath = parent.replace("\\", "/");
            if (!activatePath.startsWith("/")) {
                activatePath = "/" + activatePath;
            }
        }
        File parentDir = new File(sourceDir + activatePath);
        if (!parentDir.exists()) {
            throw new CustomException("【" + parent + "】目录不存在!");
        }
        List<FileModel> files = new ArrayList<>();
        parentDir.listFiles(new FileFilter() {
            public boolean accept(File pathname) {
                boolean fileNameOk = true;
                if (!StrUtil.isEmpty(filterName) && !pathname.getName().contains(filterName)) {
                    fileNameOk = false;
                }
                boolean fileTypeOk = true;
                if (fileType != null) {
                    FileModel.FileType activateType = pathname.isDirectory() ? FileModel.FileType.DIR : FileModel.FileType.FILE;
                    if (!fileType.equals(activateType)) {
                        fileTypeOk = false;
                    }
                }
                boolean isOk = fileNameOk && fileTypeOk;
                if (isOk) {
                    files.add(getFileModel(pathname));
                }
                return isOk;
            }
        });
        return files;
    }

    @Override
    public FileUploadBurstStartResultMode shardStart(FileUploadBurstStartMode fileUploadShardStartMode) {
        if (StrUtil.isEmpty(fileUploadShardStartMode.getMd5hash())) {
            throw new CustomException("文件md5码不能为空!");
        }
        if (fileUploadShardStartMode.getShardSize() == null || fileUploadShardStartMode.getShardSize() <= 0) {
            throw new CustomException("分片数据量不能为空!");
        }
        String md5Hash = fileUploadShardStartMode.getMd5hash();
        FileUploadBurstStartResultMode re = new FileUploadBurstStartResultMode();
        re.setFileKey(md5Hash);

        File hashDir = localUtil.getBurstDir(re.getFileKey());
        List<Integer> notUps = new ArrayList<>();
        for (Integer i = 0; i < fileUploadShardStartMode.shardSize; i++) {
            notUps.add(i);
        }
        if (!hashDir.exists()) {
            hashDir.mkdirs();
        } else {
            hashDir.list(new FilenameFilter() {
                @Override
                public boolean accept(File dir, String name) {
                    if (name.startsWith(CommonProp.SHARD_FILE_PREFIX)) {
                        try {
                            String indexStr = name.replace(CommonProp.SHARD_FILE_PREFIX, "");
                            Integer index = Integer.valueOf(indexStr);
                            if (notUps.contains(index)) {
                                notUps.remove(notUps.indexOf(index));
                            }
                            return true;
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    return false;
                }
            });
        }
        re.setNotUp(notUps);
        return re;
    }

    @Override
    public Boolean shardUpload(MultipartFile file, String fileKey, Integer shardNum) {
        if (StrUtil.isEmpty(fileKey)) {
            throw new CustomException("fileKey不能为空！");
        }
        if (shardNum == null || shardNum < 0) {
            throw new CustomException("分片编号不能为空!");
        }
        File hashDir = localUtil.getBurstDir(fileKey);
        if (!hashDir.exists() || !hashDir.isDirectory()) {
            throw new CustomException("分片存储不存在");
        }
        File shardFile = FileUtil.newFile(hashDir.getAbsolutePath() + File.separator + CommonProp.SHARD_FILE_PREFIX + shardNum);
        if (!shardFile.exists()) {
            try {
                shardFile.createNewFile();
            } catch (Exception e) {
                throw new CustomException("创建分片文件失败!");
            }
        }
        try {
            file.transferTo(shardFile);
        } catch (IOException e) {
            e.printStackTrace();
            throw new CustomException("保存文件失败!");
        }
        return true;
    }

    @Override
    public FileModel shardMerge(FileUploadMerge fileUploadMerge, int saveType) {
        if (StrUtil.isEmpty(fileUploadMerge.getFileKey())) {
            throw new CustomException("fileKey不能为空！");
        }
        //本地分片目录
        File hashDir = localUtil.getBurstDir(fileUploadMerge.getFileKey());
        if (!hashDir.exists() || !hashDir.isDirectory()) {
            throw new CustomException("分片存储不存在");
        }
        //保存在共享目录中的根路径
        String fileSavePath = fileUploadMerge.getFilePath();
        //nfs路径
        File baseDir = null;
        if (saveType == 0) {//保存到文件存储目录
            baseDir = localUtil.getFileDir();
        } else if (saveType == 1) { //保存到插件存储目录
            baseDir = localUtil.getPluginDir();
        } else {
            throw new CustomException("不支持的存储地址!");
        }
        File saveFile = new File(baseDir.getAbsolutePath() + File.separator + fileSavePath);
        if (!saveFile.getParentFile().exists()) {
            saveFile.getParentFile().mkdirs();
        }
        //创建文件,存储的nfs文件
        saveFile = FileUtil.touch(saveFile);
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(saveFile);
            boolean next = true;
            int shardIndex = 0;
            while (next) {
                String shardName = CommonProp.SHARD_FILE_PREFIX + shardIndex;
                File shardFile = new File(hashDir + File.separator + shardName);
                if (!shardFile.exists()) {
                    next = false;
                    continue;
                }
                FileInputStream in = null;
                try {
                    in = new FileInputStream(shardFile);
                    IoUtil.copy(in.getChannel(), out.getChannel());
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new CustomException("分片文件合并失败!");
                } finally {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                shardIndex++;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            throw new CustomException("共享文件保存失败！");
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return getFileModel(saveFile);

    }

    @Override
    public Boolean deleteFile(DeleteFileModel deleteFileModel) {
        if (deleteFileModel.getFilePaths() == null || deleteFileModel.getFilePaths().isEmpty()) {
            throw new CustomException("请选择要删除的文件");
        }
        File rootDir = localUtil.getFileDir();
        String rootAbs = rootDir.getAbsolutePath();
        for (String filePath : deleteFileModel.getFilePaths()) {
            if (StrUtil.isEmpty(filePath)) {
                continue;
            }
            //不能删除根目录
            if (StrUtil.equalsAny(filePath.trim(), "/", "\\")) {
                continue;
            }
            filePath = filePath.replace("\\", "/");
            try {
                File delFile = new File(rootAbs + File.separator + filePath);
                if (delFile.exists()) {
                    FileUtil.del(delFile);
                }
            } catch (Exception e) {
                if (e.getMessage().contains("AccessDeniedException:")) {
                    throw new CustomException("【" + filePath + "】没有权限删除，请确认是否正在使用！");
                }
            }
        }
        return true;
    }

    @Override
    public void downloadFile(String filePath, HttpServletResponse response) {
        File downloadFile = localUtil.getFileDir(filePath);
        if (!downloadFile.exists()) {
            throw new CustomException("【" + filePath + "】文件不存在!");
        }
        ServletOutputStream out = null;
        InputStream in = null;
        try {
            out = response.getOutputStream();
            String fileName = null;
            if (downloadFile.isDirectory()) {
                fileName = downloadFile.getName() + ".zip";
                response.setHeader("Content-Disposition", "attachment;filename=" + URLEncodeUtil.encode(fileName));
                Optional<MediaType> mediaType = MediaTypeFactory.getMediaType(fileName);
                mediaType.ifPresent(type -> response.setContentType(type.toString()));
                ZipUtil.zip(out, StandardCharsets.UTF_8, false, null, downloadFile);
            } else {
                fileName = downloadFile.getName();
                response.setHeader("Content-Disposition", "attachment;filename=" + URLEncodeUtil.encode(fileName));
                Optional<MediaType> mediaType = MediaTypeFactory.getMediaType(fileName);
                mediaType.ifPresent(type -> response.setContentType(type.toString()));
                in = new FileInputStream(downloadFile);
                IoUtil.copy(in, out);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (Exception e) {
            }
            try {
                out.flush();
            } catch (IOException e) {
            }
            try {
                out.close();
            } catch (Exception e) {
            }
        }

    }

    @Override
    public FileModel mkdirs(String path) {
        File dir = localUtil.getFileDir(path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return getFileModel(dir);
    }

    @Override
    public Boolean updateName(UpdateFileNameModel updateFileNameModel) {
        String source = updateFileNameModel.getSource();
        String target = updateFileNameModel.getTarget();
        if (StrUtil.isEmpty(source) || source.replace("\\", "/").trim().equals("/")) {
            throw new CustomException("源文件路径不规范！");
        }
        if (StrUtil.isEmpty(target) || target.replace("\\", "/").trim().equals("/")) {
            throw new CustomException("目的文件路径不规范！");
        }
        File sourceDir = localUtil.getFileDir(updateFileNameModel.getSource());
        if (!sourceDir.exists()) {
            throw new CustomException("要修改的文件不存在!");
        }
        File targetDir = localUtil.getFileDir(updateFileNameModel.getTarget());
        if (targetDir.exists()) {
            throw new CustomException("已存在同名文件!");
        }
        try {
            if (sourceDir.renameTo(targetDir)) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException("重命名错误!" + e.getMessage());
        }

    }

    @Override
    public void downloadFiles(List<String> filePaths, HttpServletResponse response) {
        OutputStream out = null;
        InputStream in = null;
        String fileName = null;
        List<File> files = new ArrayList<>();
        for (String filePath : filePaths) {
            filePath = filePath.replace("\\", "/");
            if (!filePath.startsWith("/")) {
                filePath = "/" + filePath;
            }
            File downloadFile = localUtil.getFileDir(filePath);
            if (!downloadFile.exists()) {
                throw new CustomException("【" + filePath + "】文件不存在!");
            }
            files.add(downloadFile);
        }
        if (files.isEmpty()) {
            throw new CustomException("没有可下载的文件!");
        }
        if (files.size() == 1 && files.get(0).isFile()) {
            try {
                File file = files.get(0);
                out = response.getOutputStream();
                fileName = file.getName();
                response.setHeader("Content-Disposition", "attachment;filename=" + URLEncodeUtil.encode(fileName));
                Optional<MediaType> mediaType = MediaTypeFactory.getMediaType(fileName);
                mediaType.ifPresent(type -> response.setContentType(type.toString()));
                in = new FileInputStream(file);
                IoUtil.copy(in, out);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    in.close();
                } catch (Exception e) {
                }
                try {
                    out.flush();
                } catch (IOException e) {
                }
                try {
                    out.close();
                } catch (Exception e) {
                }
            }
        } else {
            try {
                fileName = DateUtil.format(new Date(),"yyyy-MM-dd_HH_mm_ss") + ".zip";
                response.setHeader("Content-Disposition", "attachment;filename=" + URLEncodeUtil.encode(fileName));
                Optional<MediaType> mediaType = MediaTypeFactory.getMediaType(fileName);
                mediaType.ifPresent(type -> response.setContentType(type.toString()));
                out = response.getOutputStream();
                ZipUtil.zip(ZipUtil.getZipOutputStream(out,StandardCharsets.UTF_8), StandardCharsets.UTF_8, true, null, files.toArray(new File[files.size()]));
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    out.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    out.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private FileModel getFileModel(File file) {
        FileModel fileModel = new FileModel();
        fileModel.setFileName(file.getName());
        fileModel.setFileSize(file.length());
        fileModel.setLastModified(file.lastModified());
        fileModel.setFileType(file.isDirectory() ? FileModel.FileType.DIR : FileModel.FileType.FILE);
        fileModel.setSuffix(FileUtil.getSuffix(file));
        fileModel.setFilePath(localUtil.fileRelativePath(file));
        fileModel.setParent(localUtil.fileRelativePath(file.getParentFile()));
        return fileModel;
    }


}
